﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;

namespace Crayons.Components
{
    public class Layer
    {
        protected byte[] m_pixels;
        protected Bitmap m_image;
        protected Point m_position;
        protected Size m_dimension;
        protected Document m_docx;
        protected Surface mSurface;
        protected Color m_alphaColor; // use for piture 24 bit

        public Color AlphaColor
        {
            get { return m_alphaColor; }
            set { m_alphaColor = value; }
        }

        public Size Size
        {
            get { return m_dimension; }
            set 
            { 
                m_dimension = value;
                m_pixels = new byte[4 * m_dimension.Width * m_dimension.Height];
            }
        }

        public Point Position
        {
            get { return m_position; }
            set { m_position = value; }
        }

        public Bitmap LayerBitmap
        {
            get
            {
                return m_image;
            }
        }

        public Layer()
        {
        }

        public Layer(Document docx)
        {
            m_docx = docx;
        }

        public void RefreshImage()
        {
            if (m_image == null || m_image.PixelFormat != PixelFormat.Format24bppRgb) return;
            
            for (int i = 0; i < m_pixels.Length; i+=4)
            {
                if (m_pixels[i] == m_alphaColor.B &&
                    m_pixels[i + 1] == m_alphaColor.G &&
                    m_pixels[i + 2] == m_alphaColor.R
                    )
                    m_pixels[i + 3] = 0;
                else
                    m_pixels[i + 3] = 255;
            }
        }

        public Color GetPixel(int x, int y)
        {
            byte a = m_pixels[4 * (y * m_dimension.Width + x) + 3];
            byte r = m_pixels[4 * (y * m_dimension.Width + x) + 2];
            byte g = m_pixels[4 * (y * m_dimension.Width + x) + 1];
            byte b = m_pixels[4 * (y * m_dimension.Width + x)];
            return Color.FromArgb(a, r, g, b);
        }

        public static Color GetPixel(byte[] bgra, Size sz, int x, int y)
        {
            byte a = bgra[4 * (y * sz.Width + x) + 3];
            byte r = bgra[4 * (y * sz.Width + x) + 2];
            byte g = bgra[4 * (y * sz.Width + x) + 1];
            byte b = bgra[4 * (y * sz.Width + x)];
            return Color.FromArgb(a, r, g, b);
        }

        public void LoadImage(String imgPath)
        {
            m_image = new Bitmap(imgPath);
            if (m_image.PixelFormat == PixelFormat.Format32bppArgb)
            {
                BitmapData bmd = m_image.LockBits(new Rectangle(0, 0, m_image.Width, m_image.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                Size = new Size(m_image.Width, m_image.Height);
                int nbytes = Math.Min(m_dimension.Width * 4, bmd.Stride);
                for (int i = 0; i < m_image.Height; i++)
                {
                    Marshal.Copy(bmd.Scan0 + i*bmd.Stride, m_pixels, i * m_dimension.Width * 4, nbytes);
                }
                m_image.UnlockBits(bmd);
            }
            else if (m_image.PixelFormat == PixelFormat.Format24bppRgb)
            {
                BitmapData bmd = m_image.LockBits(new Rectangle(0, 0, m_image.Width, m_image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                Size = new Size(m_image.Width, m_image.Height);

                IntPtr ptr = bmd.Scan0;

                int bytes = Math.Abs(bmd.Stride) * m_image.Height;
                byte[] rgbValues = new byte[bytes];

                Marshal.Copy(ptr, rgbValues, 0, bytes);
                int f = 1;
                for (int i = 0; i < rgbValues.Length; i+=3)
                {
                    // calculate RGB
                    m_pixels[(i + f) - 1] = rgbValues[i];
                    m_pixels[(i + f)] = rgbValues[i + 1];
                    m_pixels[(i + f) + 1] = rgbValues[i + 2];
                    // default alpha
                    /*
                    // Test: set alpha where white took place
                    if (m_pixels[(i + f) - 1] == 124 &&
                        m_pixels[(i + f)] == 91 &&
                        m_pixels[(i + f) + 1] == 17
                        )
                        m_pixels[(i + f) + 2] = 0;
                    else*/
                        m_pixels[(i + f) + 2] = 255;
                    f++;
                }
                m_image.UnlockBits(bmd);
            }
        }

        public void CopyData(ref byte[] pixels, ref Rectangle visibleRec, Surface surface)
        {
            if (surface == null)
            {
                pixels = m_pixels;
            }
            else
            {
                if ((m_position.X >= 0 && (m_position.X + m_dimension.Width) <= surface.ContentSize.Width &&
                    m_position.Y >= 0 && (m_position.Y + m_dimension.Height) <= surface.ContentSize.Height))
                {
                    pixels = m_pixels;
                    return;
                }
                int l, r, t, b;

                l = Math.Abs(m_position.X);

                t = Math.Abs(m_position.Y);

                if (m_position.X + m_dimension.Width <= m_docx.ContentSize.Width)
                    r = m_dimension.Width;
                else
                    r = m_docx.ContentSize.Width - m_position.X;

                if (m_position.Y + m_dimension.Height <= m_docx.ContentSize.Height)
                    b = m_dimension.Height;
                else
                    b = m_docx.ContentSize.Height - m_position.Y;

                Rectangle rec = new Rectangle(l, t, r - l, b - t);

                byte[] res = new byte[4 * rec.Width * rec.Height];

                for (int i = 0; i < rec.Height; i++)
                {
                    int idx = 4 * ((rec.Top + i) * m_dimension.Width + rec.Left);
                    Array.Copy(m_pixels, idx, res, 4 * i * rec.Width, 4 * rec.Width);
                }

                pixels = res;
            }
        }

        public void CopyData(ref byte[] pixels, ref Rectangle visibleRec, bool dataInsideDocOnly)
        {
            if ((m_position.X >= 0 && (m_position.X + m_dimension.Width) <= m_docx.ContentSize.Width &&
                m_position.Y >= 0 && (m_position.Y + m_dimension.Height) <= m_docx.ContentSize.Height)
                || !dataInsideDocOnly)
            {
                pixels = m_pixels;
                return;
            }

            int l, r, t, b;

            l = Math.Abs(m_position.X);

            t = Math.Abs(m_position.Y);

            if (m_position.X + m_dimension.Width <= m_docx.ContentSize.Width)
                r = m_dimension.Width;
            else
                r = m_docx.ContentSize.Width - m_position.X;

            if (m_position.Y + m_dimension.Height <= m_docx.ContentSize.Height)
                b = m_dimension.Height;
            else
                b = m_docx.ContentSize.Height - m_position.Y;

            Rectangle rec = new Rectangle(l, t, r - l, b - t);

            byte[] res = new byte[4 * rec.Width * rec.Height];

            for (int i = 0; i < rec.Height; i++)
            {
                int idx = 4 * ((rec.Top + i) * m_dimension.Width + rec.Left);
                Array.Copy(m_pixels, idx, res, 4 * i * rec.Width, 4 * rec.Width);
            }

            pixels = res;
        }
    }
 
    public class LayerCollection
    {
        protected Surface mSurface;
        
        protected List<Layer> mLayers;

        protected Size mViewSize;

        protected byte[] mDisplayed;

        public Bitmap mDisplayedBitmap;

        public Layer this[int idx]
        {
            get
            {
                return mLayers[idx];
            }
        }

        public Int32 Count
        {
            get { return mLayers.Count; }
        }

        public Size ViewSize
        {
            get
            {
                return new Size(mViewSize.Width , mViewSize.Height);
            }

            set
            {
                mViewSize = value;
                mDisplayed = new byte[mViewSize.Width * mViewSize.Height * 4];
                mDisplayedBitmap = new Bitmap(mViewSize.Width + 2, mViewSize.Height + 2, PixelFormat.Format32bppArgb);
            }
        }

        public byte[] ViewData
        {
            get
            {
                return mDisplayed;
            }
        }

        public Bitmap ViewBitmap
        {
            get
            {
                return mDisplayedBitmap;
            }
        }

        public LayerCollection(Surface surface)
        {
            mSurface = surface;
            mLayers = new List<Layer>();
            mDisplayed = null;
            mDisplayedBitmap = null;
        }

        public void Prepare()
        {
            //merge layer down to layer zero
            for (int i = 0; i < mLayers.Count; i++)
            {
                byte[] layerBytes = null;
                Rectangle layerRect = Rectangle.Empty;
                mLayers[i].CopyData(ref layerBytes, ref layerRect, null);
                Utils.Blend_AlphaBlending(layerBytes, mLayers[i].Position, mLayers[i].Size, ref mDisplayed, mViewSize);
            }

            BitmapData bmdat = mDisplayedBitmap.LockBits(new Rectangle(0, 0, mDisplayedBitmap.Width, mDisplayedBitmap.Height),
                ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

            for (int i = 0; i < mViewSize.Height; i++ )
                Marshal.Copy(mDisplayed, i * mViewSize.Width * 4, 
                    bmdat.Scan0 + (bmdat.Stride * (i + 1)) + 4, mViewSize.Width * 4);

            mDisplayedBitmap.UnlockBits(bmdat);
        }

        public void AddLayer(String pathToImage)
        {
            Layer layer = new Layer();
            layer.LoadImage(pathToImage);
            mLayers.Add(layer);
        }

        public void AddLayer(Image image)
        {
        }
    }
}
